#include "HiranipraCommon.h"
#include "PhysFSCommon.h"
#include "PhysFSSystem.h"
#include "PhysFSStream.h"
#include "HCM/ComponentRegistry.h"

#include "physfs.h"
#include "globbing.h"

#include <locale>
#include <vector>

using namespace std;
using namespace Hiranipra;
using namespace Hiranipra::HCM;
using namespace Hiranipra::Platform;
using namespace Hiranipra::Platform::IO;

// {DA3CFE6F-02E7-4156-9C0D-E4EA102FF52E}
DEFINE_HCMID( implId ) = { 0xda3cfe6f, 0x2e7, 0x4156, { 0x9c, 0xd, 0xe4, 0xea, 0x10, 0x2f, 0xf5, 0x2e } };
REGISTER_COMPONENT_IMPL( Hiranipra::Platform::IO::IFileSystem, PhysFSSystem, implId );

#define TOPHYSFSSTRING( wstr ) (char*)( NarrowString( wstr ).c_str() )
#define FROMPHYSFSSTRING( pstr ) WidenString( pstr )

PhysFSSystem::PhysFSSystem()
{
}

PhysFSSystem::~PhysFSSystem()
{
	PHYSFS_deinit();
}

const wstring PhysFSSystem::GetSeparator() const
{
	return FROMPHYSFSSTRING( PHYSFS_getDirSeparator() );
}

void PhysFSSystem::Setup( const wstring& appPath )
{
	string narrow = NarrowString( appPath );
	PHYSFS_init( narrow.c_str() );
	PHYSFS_addToSearchPath( narrow.c_str(), 0 );
}

vector<wstring> PhysFSSystem::GetSearchPaths() const
{
	vector<wstring> paths;
	char** sp = PHYSFS_getSearchPath();
	for( char** p = sp; *p != NULL; p++  )
		paths.push_back( FROMPHYSFSSTRING( *p ) );
	PHYSFS_freeList( sp );
	return paths;
}

void PhysFSSystem::AddSearchPath( const wstring& path, bool insertAtHead )
{
	PHYSFS_addToSearchPath( TOPHYSFSSTRING( path ), ( insertAtHead ? 0 : 1 ) );
}

void PhysFSSystem::RemoveSearchPath( const wstring& path )
{
	PHYSFS_removeFromSearchPath( TOPHYSFSSTRING( path ) );
}

const wstring PhysFSSystem::GetWritePath() const
{
	return FROMPHYSFSSTRING( PHYSFS_getWriteDir() );
}

void PhysFSSystem::SetWritePath( const wstring& writePath )
{
	PHYSFS_setWriteDir( TOPHYSFSSTRING( writePath ) );
}

bool PhysFSSystem::MakeDirectory( const wstring& path )
{
	return PHYSFS_mkdir( TOPHYSFSSTRING( path ) ) > 0;
}

bool PhysFSSystem::Delete( const wstring& path )
{
	return PHYSFS_delete( TOPHYSFSSTRING( path ) ) > 0;
}

IFileStream* PhysFSSystem::OpenRead( const wstring& path )
{
	PHYSFS_File* handle = PHYSFS_openRead( TOPHYSFSSTRING( path ) );
	if( handle == NULL )
		return NULL;
	return new PhysFSStream( handle, true );
}

IFileStream* PhysFSSystem::OpenWrite( const wstring& path )
{
	PHYSFS_File* handle = PHYSFS_openWrite( TOPHYSFSSTRING( path ) );
	if( handle == NULL )
		return NULL;
	return new PhysFSStream( handle, false );
}

IFileStream* PhysFSSystem::OpenAppend( const wstring& path )
{
	PHYSFS_File* handle = PHYSFS_openAppend( TOPHYSFSSTRING( path ) );
	if( handle == NULL )
		return NULL;
	return new PhysFSStream( handle, false );
}

bool PhysFSSystem::Exists( const wstring& path )
{
	return PHYSFS_exists( TOPHYSFSSTRING( path ) ) > 0;
}

bool PhysFSSystem::IsDirectory( const wstring& path )
{
	return PHYSFS_isDirectory( TOPHYSFSSTRING( path ) ) > 0;
}

bool PhysFSSystem::IsSymbolicLink( const wstring& path )
{
	return PHYSFS_isSymbolicLink( TOPHYSFSSTRING( path ) ) > 0;
}

int64 PhysFSSystem::GetLastModifiedTime( const wstring& path )
{
	return PHYSFS_getLastModTime( TOPHYSFSSTRING( path ) );
}

vector<wstring> PhysFSSystem::EnumerateFiles( const wstring& path )
{
	vector<wstring> paths;
	char** sp = PHYSFS_enumerateFiles( TOPHYSFSSTRING( path ) );
	for( char** p = sp; *p != NULL; p++ )
		paths.push_back( FROMPHYSFSSTRING( *p ) );
	PHYSFS_freeList( sp );
	return paths;
}

vector<wstring> PhysFSSystem::EnumerateFiles( const wstring& path, const wstring& wildcard, bool caseSensitive )
{
	vector<wstring> paths;
	char** sp = PHYSFSEXT_enumerateFilesWildcard( TOPHYSFSSTRING( path ), TOPHYSFSSTRING( wildcard ), caseSensitive ? 1 : 0 );
	for( char** p = sp; *p != NULL; p++ )
		paths.push_back( FROMPHYSFSSTRING( *p ) );
	PHYSFS_freeList( sp );
	return paths;
}
